Package com.apps.services

Source Code of com.apps.services.AmazonWebService$SignedRequestsHelper

/*
* AUTHOR: Kevin Lam
*/

package com.apps.services;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.codec.binary.Base64;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import com.apps.ubc.cc.model.AmazonBookModel;

public class AmazonWebService {

  private static final String AWS_ACCESS_KEY_ID = "AKIAI2EIYYQQFCWV6XCA";
  private static final String AWS_SECRET_KEY = "sL57DBPNiK9T32eFHPrh06tBbPdQTVLgnYSG9sxe";
  private static final String ASSOCIATE_TAG_CA = "ubcoco03-20";
  private static final String ECS_CA = "ecs.amazonaws.ca";

  private static final int ISBN_LOOKUP = 0x1;
  private static final int PRICE_LOOKUP = 0x2;
  private static final int IMAGE_LOOKUP = 0x3;

  private static final int FETCH_ASIN = 0x1;
  private static final int FETCH_TITLE = 0x2;
  private static final int FETCH_DETAIL = 0x3;
  private static final int FETCH_USED_PRICE = 0x4;
  private static final int FETCH_NEW_PRICE = 0x5;
  private static final int FETCH_IMAGE = 0x6;
 
  private SignedRequestsHelper helper;
 
  public AmazonWebService(){
    try {
      helper = SignedRequestsHelper.getInstance(ECS_CA,
          AWS_ACCESS_KEY_ID, AWS_SECRET_KEY);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
 
  public AmazonBookModel search(String keyword) {
    String requestURL = lookUpKeyword(keyword);
    String title = fetch(requestURL, FETCH_TITLE);
    String detail = fetch(requestURL, FETCH_DETAIL);
    String asin = fetch(requestURL, FETCH_ASIN);
    requestURL = lookUpPrice(asin,PRICE_LOOKUP);
    String priceNew = fetch(requestURL, FETCH_NEW_PRICE);
    String priceUsed = fetch(requestURL, FETCH_USED_PRICE);
    requestURL = lookUpImage(asin);
    String imageUrl = fetch(requestURL, FETCH_IMAGE);
    AmazonBookModel abm = new AmazonBookModel(asin, title, imageUrl,
        detail, priceNew, priceUsed);
    return abm;
  }

  private  String lookUpKeyword(String keywords) {
    String requestUrl = null;
    Map<String, String> params = getLookupMap(ISBN_LOOKUP);
    params.put("Keywords", keywords);
    if (helper != null)
      requestUrl = helper.sign(params);
    return requestUrl;
  }

  private String lookUpPrice(String asin, int type) {
    String requestUrl = null;
    Map<String, String> params = getLookupMap(type);
    params.put("ItemId", asin);
    if (helper != null)
      requestUrl = helper.sign(params);
    return requestUrl;
  }

  private  String lookUpImage(String asin) {
    String requestUrl = null;
    Map<String, String> params = getLookupMap(IMAGE_LOOKUP);
    params.put("ItemId", asin);
    if (helper != null)
      requestUrl = helper.sign(params);
    return requestUrl;
  }

  private String fetch(String requestUrl, int type) {
    String result = "";
    if(requestUrl == null || requestUrl.equals(""))
      return "";
    try {
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      DocumentBuilder db = dbf.newDocumentBuilder();
      Document doc = db.parse(requestUrl);
      Node node = null;
      switch (type) {
      case FETCH_ASIN:
        node = doc.getElementsByTagName("ASIN").item(0);
        break;
      case FETCH_DETAIL:
        node = doc.getElementsByTagName("DetailPageURL").item(0);
        break;
      case FETCH_IMAGE:
        Node parentNode = doc.getElementsByTagName("SmallImage")
            .item(0);
        if (parentNode != null)
          node = parentNode.getChildNodes().item(0);
        break;
      case FETCH_NEW_PRICE:
        Node lowestNewPriceNode = doc.getElementsByTagName(
            "LowestNewPrice").item(0);
        if (lowestNewPriceNode != null)
          node = lowestNewPriceNode.getLastChild();
        break;
      case FETCH_USED_PRICE:
        Node offerListingIdNode = doc.getElementsByTagName(
            "OfferListingId").item(0);
        if (offerListingIdNode != null)
          node = offerListingIdNode.getNextSibling().getLastChild();
        break;
      case FETCH_TITLE:
        node = doc.getElementsByTagName("Title").item(0);
        break;
      }
      if (node != null)
        result = node.getTextContent();

    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return result;
  }

  private HashMap<String, String> getLookupMap(int mapEnum) {

    HashMap<String, String> params = new HashMap<String, String>();
    params.put("AssociateTag", ASSOCIATE_TAG_CA);
    params.put("Service", "AWSECommerceService");
    params.put("Version", "2009-03-31");
    switch (mapEnum) {
    case ISBN_LOOKUP:
      params.put("IdType", "ISBN");
      params.put("SearchIndex", "Books");
      params.put("Operation", "ItemSearch");
      break;
    case PRICE_LOOKUP:
      params.put("IdType", "ASIN");
      params.put("Operation", "ItemLookup");
      params.put("ResponseGroup", "Offers");
      params.put("Condition", "Used");
      break;
    case IMAGE_LOOKUP:
      params.put("IdType", "ASIN");
      params.put("Operation", "ItemLookup");
      params.put("ResponseGroup", "Images");
      params.put("Condition", "All");
      break;
    }
    return params;
  }

  // SignedRequestHelper taken from Amazon documentation
   static class SignedRequestsHelper {
    /**
     * All strings are handled as UTF-8
     */
    private static final String UTF8_CHARSET = "UTF-8";

    /**
     * The HMAC algorithm required by Amazon
     */
    private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";

    /**
     * This is the URI for the service, don't change unless you really know
     * what you're doing.
     */
    private static final String REQUEST_URI = "/onca/xml";

    /**
     * The sample uses HTTP GET to fetch the response. If you changed the
     * sample to use HTTP POST instead, change the value below to POST.
     */
    private static final String REQUEST_METHOD = "GET";

    private String endpoint = null;
    private String awsAccessKeyId = null;
    private String awsSecretKey = null;

    private SecretKeySpec secretKeySpec = null;
    private Mac mac = null;

    /**
     * You must provide the three values below to initialize the helper.
     *
     * @param endpoint
     *            Destination for the requests.
     * @param awsAccessKeyId
     *            Your AWS Access Key ID
     * @param awsSecretKey
     *            Your AWS Secret Key
     */
    public static SignedRequestsHelper getInstance(String endpoint,
        String awsAccessKeyId, String awsSecretKey)
        throws IllegalArgumentException, UnsupportedEncodingException,
        NoSuchAlgorithmException, InvalidKeyException {
      if (null == endpoint || endpoint.length() == 0) {
        throw new IllegalArgumentException("endpoint is null or empty");
      }
      if (null == awsAccessKeyId || awsAccessKeyId.length() == 0) {
        throw new IllegalArgumentException(
            "awsAccessKeyId is null or empty");
      }
      if (null == awsSecretKey || awsSecretKey.length() == 0) {
        throw new IllegalArgumentException(
            "awsSecretKey is null or empty");
      }

      SignedRequestsHelper instance = new SignedRequestsHelper();
      instance.endpoint = endpoint.toLowerCase();
      instance.awsAccessKeyId = awsAccessKeyId;
      instance.awsSecretKey = awsSecretKey;

      byte[] secretyKeyBytes = instance.awsSecretKey
          .getBytes(UTF8_CHARSET);
      instance.secretKeySpec = new SecretKeySpec(secretyKeyBytes,
          HMAC_SHA256_ALGORITHM);
      instance.mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
      instance.mac.init(instance.secretKeySpec);

      return instance;
    }

    /**
     * The construct is private since we'd rather use getInstance()
     */
    private SignedRequestsHelper() {
    }

    /**
     * This method signs requests in hashmap form. It returns a URL that
     * should be used to fetch the response. The URL returned should not be
     * modified in any way, doing so will invalidate the signature and
     * Amazon will reject the request.
     */
    public String sign(Map<String, String> params) {
      // Let's add the AWSAccessKeyId and Timestamp parameters to the
      // request.
      params.put("AWSAccessKeyId", this.awsAccessKeyId);
      params.put("Timestamp", this.timestamp());

      // The parameters need to be processed in lexicographical order, so
      // we'll
      // use a TreeMap implementation for that.
      SortedMap<String, String> sortedParamMap = new TreeMap<String, String>(
          params);

      // get the canonical form the query string
      String canonicalQS = this.canonicalize(sortedParamMap);

      // create the string upon which the signature is calculated
      String toSign = REQUEST_METHOD + "\n" + this.endpoint + "\n"
          + REQUEST_URI + "\n" + canonicalQS;

      // get the signature
      String hmac = this.hmac(toSign);
      String sig = this.percentEncodeRfc3986(hmac);

      // construct the URL
      String url = "http://" + this.endpoint + REQUEST_URI + "?"
          + canonicalQS + "&Signature=" + sig;

      return url;
    }

    /**
     * This method signs requests in query-string form. It returns a URL
     * that should be used to fetch the response. The URL returned should
     * not be modified in any way, doing so will invalidate the signature
     * and Amazon will reject the request.
     */
    public String sign(String queryString) {
      // let's break the query string into it's constituent name-value
      // pairs
      Map<String, String> params = this.createParameterMap(queryString);

      // then we can sign the request as before
      return this.sign(params);
    }

    /**
     * Compute the HMAC.
     *
     * @param stringToSign
     *            String to compute the HMAC over.
     * @return base64-encoded hmac value.
     */
    private String hmac(String stringToSign) {
      String signature = null;
      byte[] data;
      byte[] rawHmac;
      try {
        data = stringToSign.getBytes(UTF8_CHARSET);
        rawHmac = mac.doFinal(data);
        Base64 encoder = new Base64();
        signature = new String(encoder.encode(rawHmac));
      } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(UTF8_CHARSET + " is unsupported!", e);
      }
      return signature;
    }

    /**
     * Generate a ISO-8601 format timestamp as required by Amazon.
     *
     * @return ISO-8601 format timestamp.
     */
    private String timestamp() {
      String timestamp = null;
      Calendar cal = Calendar.getInstance();
      DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
      dfm.setTimeZone(TimeZone.getTimeZone("GMT"));
      timestamp = dfm.format(cal.getTime());
      return timestamp;
    }

    /**
     * Canonicalize the query string as required by Amazon.
     *
     * @param sortedParamMap
     *            Parameter name-value pairs in lexicographical order.
     * @return Canonical form of query string.
     */
    private String canonicalize(SortedMap<String, String> sortedParamMap) {
      if (sortedParamMap.isEmpty()) {
        return "";
      }

      StringBuffer buffer = new StringBuffer();
      Iterator<Map.Entry<String, String>> iter = sortedParamMap
          .entrySet().iterator();

      while (iter.hasNext()) {
        Map.Entry<String, String> kvpair = iter.next();
        buffer.append(percentEncodeRfc3986(kvpair.getKey()));
        buffer.append("=");
        buffer.append(percentEncodeRfc3986(kvpair.getValue()));
        if (iter.hasNext()) {
          buffer.append("&");
        }
      }
      String cannoical = buffer.toString();
      return cannoical;
    }

    /**
     * Percent-encode values according the RFC 3986. The built-in Java
     * URLEncoder does not encode according to the RFC, so we make the extra
     * replacements.
     *
     * @param s
     *            decoded string
     * @return encoded string per RFC 3986
     */
    private String percentEncodeRfc3986(String s) {
      String out;
      try {
        out = URLEncoder.encode(s, UTF8_CHARSET).replace("+", "%20")
            .replace("*", "%2A").replace("%7E", "~");
      } catch (UnsupportedEncodingException e) {
        out = s;
      }
      return out;
    }

    /**
     * Takes a query string, separates the constituent name-value pairs and
     * stores them in a hashmap.
     *
     * @param queryString
     * @return
     */
    private Map<String, String> createParameterMap(String queryString) {
      Map<String, String> map = new HashMap<String, String>();
      String[] pairs = queryString.split("&");

      for (String pair : pairs) {
        if (pair.length() < 1) {
          continue;
        }

        String[] tokens = pair.split("=", 2);
        for (int j = 0; j < tokens.length; j++) {
          try {
            tokens[j] = URLDecoder.decode(tokens[j], UTF8_CHARSET);
          } catch (UnsupportedEncodingException e) {
          }
        }
        switch (tokens.length) {
        case 1: {
          if (pair.charAt(0) == '=') {
            map.put("", tokens[0]);
          } else {
            map.put(tokens[0], "");
          }
          break;
        }
        case 2: {
          map.put(tokens[0], tokens[1]);
          break;
        }
        }
      }
      return map;
    }
  }

}
TOP

Related Classes of com.apps.services.AmazonWebService$SignedRequestsHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.